<template>
    <div>
        <div>
            <button @click="handleRotate(-90)">
                -90
            </button>
            <button @click="handleRotate(90)">
                90
            </button>
        </div>
        <div>
            <button @click="handleCropAction">
                crop
            </button>
        </div>
        <div class="editor-container" ref="container"
        >
            <div id="konva--body" ref="body"></div>
            <div v-if="imageObj.shape" class="crop-container" :style="getCropContainerStyle">
                <div class="top-left-point control-point"
                     @mousedown="handleCropPointMouseDown('top-left',$event)"
                ></div>
                <div class="top-right-point control-point"
                     @mousedown="handleCropPointMouseDown('top-right',$event)"
                ></div>
                <div class="bottom-left-point control-point"
                     @mousedown="handleCropPointMouseDown('bottom-left',$event)"
                ></div>
                <div class="bottom-right-point control-point"
                     @mousedown="handleCropPointMouseDown('bottom-right',$event)"
                ></div>
            </div>
        </div>
    </div>
</template>

<script>
    import Konva                                                          from 'konva';
    import { asyncImageLoader, autoSize, cloneOf, damping, rectBoxLimit } from "../Utilys/methods";


    const DEFAULT_SET = {
        image_object: {
            shape  : null,
            element: null,
            height : null,
            width  : null,
        },
        stage_object: {
            shape : null,
            height: null,
            width : null,
        },
        layer_object: {
            shape: null,
        },
        crop_object : {
            layer: null,
            shape: null,
        },
    };

    export default {
        name    : "konva-editor-2",
        data() {
            return {
                imageObj             : cloneOf(DEFAULT_SET.image_object),
                stageObj             : cloneOf(DEFAULT_SET.stage_object),
                layerObj             : cloneOf(DEFAULT_SET.layer_object),
                cropObj              : cloneOf(DEFAULT_SET.crop_object),
                rotationTween        : null,
                scalePositionTween   : null,
                prevRotation         : null,
                cropActive           : true,
                cropMovePoint        : '',
                cropContainerPosition: {
                    left  : 100,
                    top   : 100,
                    right : 600,
                    bottom: 600,
                },
            };
        },
        mounted() {
            this.LoadImage("/images/test1.jpg");
            document.addEventListener('mouseup', this.handleCropPointMouseUp.bind(this));
            document.addEventListener('mousemove', this.handleCropPointMouseMove.bind(this));
        },
        computed: {
            getContainerSize() {
                let { container } = this.$refs;

                return {
                    width : container.offsetWidth,
                    height: container.offsetHeight,
                }
            },
            getImageSize() {
                let image             = this.imageObj.shape;
                let scale             = image.scale();
                let rotation          = image.rotation();
                let offset            = image.offset();
                let { width, height } = image.size();

                let currentWidth   = width * scale.x;
                let currentHeight  = height * scale.y;
                let currentOffsetX = offset.x * scale.x;
                let currentOffsetY = offset.y * scale.y;


                let offsetValue = {
                    currentOffsetX,
                    currentOffsetY,
                };
                if (rotation && rotation % 90 === 0) {
                    offsetValue.currentOffsetX = currentOffsetY;
                    offsetValue.currentOffsetY = currentOffsetX;
                }

                return {
                    rectSize: {
                        currentWidth,
                        currentHeight,
                    },
                    offsetValue,
                }
            },
            getCropContainerStyle() {
                let { width: clientWidth, height: clientHeight } = this.getContainerSize;
                let { top, left, right, bottom }                 = this.cropContainerPosition;
                let strokeWidth                                  = clientWidth > clientHeight ? clientWidth : clientHeight;

                return {
                    boxShadow: `rgba(255,255,255,.6) 0 0 0 ${ strokeWidth }px`,
                    left     : `${ left }px`,
                    top      : `${ top }px`,
                    width    : `${ right - left }px`,
                    height   : `${ bottom - top }px`,
                }
            }
        },
        methods : {

            handleCropAction() {
                let { top, left, right, bottom } = this.cropContainerPosition;
                let image                        = this.imageObj.shape;
                let scale                        = image.scaleX();
                let height                       = bottom - top;
                let width                        = right - left;

                let attr = {
                    cropX     : left,
                    cropY     : top,
                    cropWidth : width,
                    cropHeight: height,
                };

                console.log('attr :', attr);
                image.setAttrs(attr);
                this.layerObj.shape.draw();
            },
            handleCropPointMouseDown(point, event) {
                event.preventDefault();
                event.stopPropagation();
                this.cropMovePoint = point;
            },
            handleCropPointMouseUp() {
                console.log('clear :');
                this.cropMovePoint = '';
            },
            handleCropPointMouseMove(event) {
                if (!this.cropMovePoint) return;
                event.preventDefault();
                event.stopPropagation();

                let [ vertical, horizon ]                        = this.cropMovePoint.split('-');
                let size                                         = this.cropContainerPosition;
                let width                                        = size.right - size.left;
                let height                                       = size.bottom - size.top;
                let { width: clientWidth, height: clientHeight } = this.getContainerSize;
                let { rectSize, offsetValue }                    = this.getImageSize;
                let imagePosition                                = this.imageObj.shape.position();

                imagePosition.x -= offsetValue.currentOffsetX;
                imagePosition.y -= offsetValue.currentOffsetY;
                let imageBottom = imagePosition.y + rectSize.currentHeight;
                let imageRight  = imagePosition.x + rectSize.currentWidth;

                if (vertical === 'bottom') {
                    size.bottom += event.movementY;
                    if (size.bottom - size.top < 50) size.bottom = size.top + 50;

                    if (rectSize.currentHeight < clientHeight) {
                        if (size.bottom > imageBottom) size.bottom = imageBottom;
                    } else {
                        if (imageBottom < clientHeight) {
                            if (size.bottom > imageBottom) size.bottom = imageBottom;
                        } else {
                            if (size.bottom > clientHeight) size.bottom = clientHeight;
                        }
                    }


                } else if (vertical === 'top') {
                    size.top += event.movementY;

                    if (rectSize.currentHeight < clientHeight) {
                        if (imagePosition.y > size.top) size.top = imagePosition.y;
                    } else {
                        if (imagePosition.y > 0) {
                            if (imagePosition.y > size.top) size.top = imagePosition.y;
                        } else {
                            if (size.top < 0) size.top = 0;
                        }
                    }

                    if (size.top + 50 > size.bottom) size.top = size.bottom - 50;

                }

                if (horizon === 'right') {
                    size.right += event.movementX;

                    if (rectSize.currentWidth < clientWidth) {
                        if (size.right > imageRight) size.right = imageRight;
                    } else {
                        if (imageRight < clientWidth) {
                            if (size.right > imageRight) size.right = imageRight;
                        } else {
                            if (size.right > clientWidth) size.right = clientWidth;
                        }
                    }

                    if (size.right - size.left < 50) size.right = size.left + 50;
                } else if (horizon === 'left') {
                    size.left += event.movementX;

                    if (rectSize.currentWidth < clientWidth) {
                        if (imagePosition.x > size.left) size.left = imagePosition.x;
                    } else {
                        if (imagePosition.x > 0) {
                            if (imagePosition.x > size.left) size.left = imagePosition.x;
                        } else {
                            if (size.left < 0) size.left = 0;
                        }
                    }

                    if (size.left + 50 > size.right) size.left = size.right - 50;
                }
            },
            async mapToLoadImageElement(url) {
                let image = await asyncImageLoader(url);

                this.imageObj.element = image;
                this.imageObj.height  = image.height;
                this.imageObj.width   = image.width;
            },
            mapToMakeCrop() {
                let { width: clientWidth, height: clientHeight } = this.getContainerSize;
                let strokeWidth                                  = clientWidth > clientHeight ? clientWidth : clientHeight;

                strokeWidth         = 100;
                let cropShape       = new Konva.Rect({
                    width : 500,
                    height: 500,
                    fill  : '#fff',
                });
                let cropTransformer = new Konva.Transformer({
                    node: cropShape,
                });

                this.layerObj.shape.add(cropTransformer);

            },
            mapToMakeStage() {
                if (this.stageObj.shape)
                    this.stageObj.shape.remove();

                let { width, height } = this.getContainerSize;
                let stage             = new Konva.Stage({
                    container: 'konva--body',
                    width,
                    height,
                });

                this.stageObj.shape = stage;
            },
            mapToMakeLayer() {
                if (this.layerObj.shape)
                    this.layerObj.shape.remove();

                const layer = new Konva.Layer();
                this.stageObj.shape.add(layer);

                this.layerObj.shape = layer;
            },
            mapToMakeImage() {
                let containerSize                                      = this.getContainerSize;
                let { width: containerWidth, height: containerHeight } = containerSize;
                let { width, height }                                  = autoSize(this.imageObj, containerSize);
                let layer                                              = this.layerObj.shape;


                let offsetX = width / 2;
                let offsetY = height / 2;

                let backX = 0;
                let backY = 0;

                const image = new Konva.Image({
                    image        : this.imageObj.element,
                    width,
                    height,
                    x            : containerWidth / 2,
                    y            : containerHeight / 2,
                    offset       : {
                        x: offsetX,
                        y: offsetY,
                    },
                    scale        : {
                        x: 2.5,
                        y: 2.5,
                    },
                    draggable    : true,
                    dragBoundFunc: (pos) => {
                        let { rectSize, offsetValue } = this.getImageSize;

                        let { differenceY, differenceX, x, y } = rectBoxLimit(pos, rectSize, {
                            containerWidth,
                            containerHeight,
                        }, offsetValue);

                        if (differenceY) {
                            backY = y;
                            y += damping(differenceY, containerHeight / 2);
                        } else if (backY) backY = 0;


                        if (differenceX) {
                            backX = x;
                            x += damping(differenceX, containerWidth / 2);
                        } else if (backX) backX = 0;

                        return {
                            x,
                            y
                        };
                    },
                });
                image.on('dragend', (e) => {
                    if (backX || backY) {
                        let options = {
                            node    : image,
                            duration: .5,
                            easing  : Konva.Easings[ 'EaseOut' ]
                        };
                        if (backX) options.x = backX;
                        if (backY) options.y = backY;
                        (new Konva.Tween(options)).play();
                    }
                    backX = 0;
                    backY = 0;
                });
                this.stageObj.shape.on('wheel', (e) => {
                    let evt = e.evt;
                    evt.preventDefault();
                    evt.stopPropagation();
                    let scale = image.scaleX();
                    scale += evt.deltaY * -0.01;

                    scale = Math.min(Math.max(.125, scale), 4);

                    let { rectSize, offsetValue } = this.getImageSize;
                    let pos                       = image.position();

                    let { x, y } = rectBoxLimit(pos, rectSize, {
                        containerWidth,
                        containerHeight,
                    }, offsetValue);

                    if ((x !== pos.x || y !== pos.y) && !this.scalePositionTween) {
                        this.scalePositionTween = new Konva.Tween({
                            node    : image,
                            easing  : Konva.Easings[ 'EaseOut' ],
                            duration: .4,
                            x,
                            y,
                            onFinish: () => {
                                this.scalePositionTween = null
                            }
                        });
                        this.scalePositionTween.play();
                    }

                    image.scale({
                        x: scale,
                        y: scale,
                    });
                    layer.draw();
                });

                layer.add(image);
                this.imageObj.shape = image;
            },
            async LoadImage(url) {
                await this.mapToLoadImageElement(url);
                this.mapToMakeStage();
                this.mapToMakeLayer();
                this.mapToMakeImage();
                // this.mapToMakeCrop();

                this.stageObj.shape.batchDraw();
            },
            handleRotate(deg) {
                let { width: clientWidth, height: clientHeight } = this.getContainerSize;
                let { shape: image }                             = this.imageObj;

                let { width: imageWidth, height: imageHeight } = image.size();
                let rotation                                   = image.rotation();

                if (this.prevRotation && rotation !== this.prevRotation) {
                    rotation = this.prevRotation;
                }
                if (this.rotationTween) {
                    this.rotationTween.destroy();
                }

                rotation += deg;

                let imageSize = {
                    width : imageWidth,
                    height: imageHeight,
                };
                let b         = rotation && rotation % 90 === 0;
                if (b) {
                    imageSize.width  = imageHeight;
                    imageSize.height = imageWidth;
                }
                let size = autoSize(imageSize, this.getContainerSize);


                let width  = !b ? size.width : size.height;
                let height = !b ? size.height : size.width;

                let options       = {
                    node    : image,
                    duration: .5,
                    scaleX  : 1,
                    scaleY  : 1,
                    offsetX : width / 2,
                    offsetY : height / 2,
                    width,
                    height,
                    x       : clientWidth / 2,
                    y       : clientHeight / 2,
                    rotation: rotation,
                    easing  : Konva.Easings[ 'EaseOut' ]
                };
                this.prevRotation = rotation;

                this.rotationTween = new Konva.Tween(options);
                this.rotationTween.play();
            },


        },
    }
</script>

<style scoped lang="less">
    .editor-container {
        position: relative;
        width: 100%;
        height: 700px;
        background-color: #000;
        overflow: hidden;
    }

    .crop-container {
        position: absolute;
        top: 500px;
        left: 200px;

        width: 500px;
        height: 500px;

        .control-point {
            position: absolute;
            width: 20px;
            height: 20px;
            border-radius: 100%;
            background: #fff;
            z-index: 1;
            box-shadow: rgba(255, 255, 255, .6) 0px 0px 3px 2px;
            transition: transform .2s linear;

            &:hover {
                transform: scale(1.2);
            }
        }

        .bottom-left-point,
        .top-left-point {
            left: -10px;
        }

        .top-left-point,
        .top-right-point {
            top: -10px;
        }

        .top-right-point,
        .bottom-right-point {
            right: -10px;
        }

        .bottom-left-point,
        .bottom-right-point {
            bottom: -10px;
        }

    }


</style>
